package com.sgcc.epri.dcloud.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sgcc.epri.dcloud.mapper.dm.*;
import com.sgcc.epri.dcloud.mapper.mpp.AccMapper;
import com.sgcc.epri.dcloud.pojo.*;
import com.sgcc.epri.dcloud.util.ChoreUtil;
import com.sgcc.epri.dcloud.util.RestTemplateUtil;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

@Service
public class ComputationalLogic {

    private final Logger log =  LoggerFactory.getLogger(this.getClass());

    @Autowired
    private CalResultAllMapper calResultAllMapper;

    @Autowired
    private FractureSurfaceOrcMapper fractureSurfaceOrcMapper;

    @Autowired
    private AccuracyRateContentMapper accuracyRateContentMapper;

    @Autowired
    private AccMapper accMapper;

    @Autowired
    private Mapper mapper;

    @Autowired
    private RestTemplateUtil restTemplateUtil;

    @Autowired
    private ThreadPoolTaskExecutor thread;

    @Autowired
    @Qualifier("dmSqlSessionFactory")
    private SqlSessionFactory sqlSessionFactory;

    @Value("${dm.batch-size}")
    private int batchSize;

    //===================================================电科院完整率===================================================================
    /**
     * 电科院完整率
     * @param treeNode 当前树形过结构对象
     * @param managementUnits 数据源
     * @param param 统计范围
     * @param tableNameMap 映射表名
     * @param tableHead 当前表头
     * @param mx 此参数不为空则代表此次查询只查华北不含蒙西的数
     */
    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Object> dkyAccessRate(TreeNode treeNode, List<ManagementUnit> managementUnits, List<AssetTypeParamCenter> param,
                              Map<String, Object> tableNameMap, TableHead tableHead, String mx, String indexId){
        try {
            // 校验
            if(ChoreUtil.checkParam(treeNode,tableHead,managementUnits,param)) return CompletableFuture.completedFuture("error");
            // 查询的基本参数
            String objectId = tableHead.getObjectId();
            String tableNameCenter = tableNameMap.get(tableHead.getObjectId()).toString();   // 表名
            log.info("表名：{}，参数：{}",tableNameCenter, param);
            // 总数
            CompletableFuture<Object> objectCompletableFuture = dkyAccessRateMain(treeNode, managementUnits, param, tableHead, mx, objectId, tableNameCenter, indexId);
            // 一级详情
            CompletableFuture<Object> objectCompletableFuture1 = dkyAccessRateOneLevelContext(treeNode, managementUnits, param, tableHead, mx, objectId, tableNameCenter, indexId);
            // 二级详情
            CompletableFuture<Object> objectCompletableFuture2 = dkyAccessRateTwoLevelContext(treeNode, managementUnits, param, tableHead, mx, objectId, tableNameCenter, indexId);

            CompletableFuture.allOf(objectCompletableFuture, objectCompletableFuture1, objectCompletableFuture2).join();
//            CompletableFuture.allOf(objectCompletableFuture).join();

            if (mx == null) {
                dkyAccessRate(treeNode, managementUnits, param, tableNameMap, tableHead, "mx", indexId);
            }
        }catch (Exception e){
            e.printStackTrace();
            log.error("在计算电科院接入率时出错!");
        }
        return CompletableFuture.completedFuture("success");

    }

    /**
     * 总数
     * @param treeNode
     * @param managementUnits
     * @param param
     * @param tableHead
     * @param mx
     * @param objectId
     * @param tableNameCenter
     */
    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Object> dkyAccessRateMain(TreeNode treeNode, List<ManagementUnit> managementUnits, List<AssetTypeParamCenter> param,
                                  TableHead tableHead, String mx, String objectId, String tableNameCenter, String indexId){

        List<Map<String, Object>> integrality = null;

        // 断面特殊处理，每个资产类型要单独跑一遍，边循环边整合
        if (objectId.equals("0401")) {
            for (AssetTypeParamCenter map : param) {
                // 资产id
                String assetId = map.getAssetId();
                // 查询资产相关的设备id
                List<FractureSurfaceOrc> fractureSurfaceOrcs = fractureSurfaceOrcMapper.selectList(new QueryWrapper<FractureSurfaceOrc>().eq("asset_id", assetId));
                if (integrality == null) {
                    integrality = mapper.integrality(tableNameCenter, ChoreUtil.strToArray(assetId), mx, managementUnits, fractureSurfaceOrcs);

                    continue;
                }
                List<Map<String, Object>> integralitySum = mapper.integrality(tableNameCenter, ChoreUtil.strToArray(assetId), mx, managementUnits, fractureSurfaceOrcs);
                // 合并数据
                integrality = ChoreUtil.mergeDataIntegrality(integrality, integralitySum);
            }
        }else{
            integrality = mapper.integrality(tableNameCenter, param, mx, managementUnits, null);
        }

        String now = DateUtil.now();
        //转换为对象
        List<CalResultAll> calResultAlls = new ArrayList<CalResultAll>();
        for (Map<String, Object> som : integrality) {
            CalResultAll cra = new CalResultAll();
            cra.setParentIdTree(treeNode.getId()+"");
            if (mx != null && mx.equals("mx")) {
                if (som.get("id").toString().equals("0021990101")) {
                    cra.setDatasource("0021990101_no");
                }
            }else{
                cra.setDatasource(som.get("id")+"");
            }
            cra.setParentIdTablehead(tableHead.getId()+"");
            cra.setParentIdIndex(indexId);
            if (indexId.equals("1001")) {
                cra.setValue(som.get("access_rate")+"");
                cra.setMolecule(som.get("actual_asset_num")+"");
                cra.setDenominator(som.get("asset_num")+"");
            } else if (indexId.equals("1002")) {
                cra.setValue(som.get("days_rate")+"");
                cra.setMolecule(som.get("actual_days_num")+"");
                cra.setDenominator(som.get("days_num")+"");
            } else if (indexId.equals("1004")) {
                cra.setValue(som.get("asset_num")+"");
                cra.setMolecule(som.get("asset_num")+"");
                cra.setDenominator(som.get("asset_num")+"");
            }
            cra.setObjectId(objectId);
            cra.setDataType("dky");
            cra.setDateType("allday");
            cra.setCreateTime(now);
            calResultAlls.add(cra);
        }

        log.info(mx+"  "+calResultAlls);

        // 关闭session的自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        CalResultAllMapper calResultAllMapper = sqlSession.getMapper(CalResultAllMapper.class);

        try{

            QueryWrapper<CalResultAll> craqw = new QueryWrapper<>();
            craqw.eq("parent_id_tree", treeNode.getId());
            craqw.eq("parent_id_tablehead", tableHead.getId());
            craqw.eq("parent_id_index", indexId);
            craqw.eq("object_id",objectId);
            craqw.eq("data_type", "dky");
            craqw.eq("date_type", "allday");

            // 此方法会被调用两次，只有首次执行删除操作。
            if (ChoreUtil.isAlpha(mx)) {
                calResultAllMapper.delete(craqw);
            }

            int i = 0;
            for (CalResultAll calResultAll : calResultAlls) {
                calResultAllMapper.insert(calResultAll);
                if (i != 0 && i%batchSize == 0){
                    log.info("[{}/{}] 电科院{}_总数", i, calResultAlls.size(),indexId);
                }
                i++;
            }
            // 提交数据
            sqlSession.commit();
            log.info("[{}/{}] 电科院{}_总数", i, calResultAlls.size(),indexId);

        }catch (Exception e){
            sqlSession.rollback();
            e.printStackTrace();
            log.error("任务出错!"+e);
        }finally {
            sqlSession.close();
        }
        return CompletableFuture.completedFuture("success");
    }

    /**
     * 详情一
     * @param treeNode
     * @param managementUnits
     * @param param
     * @param tableHead
     * @param mx
     * @param objectId
     * @param tableNameCenter
     */
    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Object> dkyAccessRateOneLevelContext(TreeNode treeNode, List<ManagementUnit> managementUnits, List<AssetTypeParamCenter> param,
                                             TableHead tableHead, String mx, String objectId, String tableNameCenter, String indexId){
        List<Map<String, Object>> integrality = null;

        // 断面特殊处理，每个资产类型要单独跑一边，边循环边整合
        if (objectId.equals("0401")) {
            for (AssetTypeParamCenter map : param) {
                // 资产id
                String assetId = map.getAssetId();
                // 查询资产相关的设备id
                List<FractureSurfaceOrc> fractureSurfaceOrcs = fractureSurfaceOrcMapper.selectList(new QueryWrapper<FractureSurfaceOrc>().eq("asset_id", assetId));
                if (integrality == null) {
                    integrality = mapper.integralityOneLevel(tableNameCenter, ChoreUtil.strToArray(assetId), mx, managementUnits, fractureSurfaceOrcs);
                    continue;
                }
                // 合并数据
                integrality = ChoreUtil.mergeDataIntegrality(integrality, mapper.integralityOneLevel(tableNameCenter, ChoreUtil.strToArray(assetId), mx, managementUnits, fractureSurfaceOrcs));
            }
        }else{
            integrality = mapper.integralityOneLevel(tableNameCenter, param, mx, managementUnits, null);
        }

        String now = DateUtil.now();

        //转换为对象
        List<IntegralityOneLevel> calResultAlls = new ArrayList<IntegralityOneLevel>();
        for (Map<String, Object> som : integrality) {
            IntegralityOneLevel cra = new IntegralityOneLevel();
            cra.setParentIdTree(treeNode.getId()+"");
            if (mx != null && mx.equals("mx")) {
                cra.setDatasource("0021990101_no");
            }else{
                cra.setDatasource(som.get("id")+"");
            }
            cra.setParentIdTableHead(tableHead.getId()+"");
            cra.setAssetId(som.get("asset_id")+"");
            cra.setAssetName(som.get("asset_name")+"");
            cra.setUpdensityName(som.get("name")+"");
            cra.setIndexId(indexId);
            if (indexId.equals("1001")) {
                cra.setValue(som.get("access_rate")+"");
                cra.setMolecule(som.get("actual_asset_num")+"");
                cra.setDenominator(som.get("asset_num")+"");
            } else if (indexId.equals("1002")) {
                cra.setValue(som.get("days_rate")+"");
                cra.setMolecule(som.get("actual_days_num")+"");
                cra.setDenominator(som.get("days_num")+"");
            } else if (indexId.equals("1004")) {
                cra.setValue(som.get("asset_num")+"");
                cra.setMolecule(som.get("asset_num")+"");
                cra.setDenominator(som.get("asset_num")+"");
            }
            cra.setObjectId(objectId);
            cra.setDataType("dky");
            cra.setDateType("allday");
            cra.setCreateTime(now);
            calResultAlls.add(cra);
        }

        // 关闭session的自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        IntegralityOneLevelMapper integralityOneLevelMapper = sqlSession.getMapper(IntegralityOneLevelMapper.class);

        try{
            QueryWrapper<IntegralityOneLevel> craqw = new QueryWrapper<>();
            craqw.eq("parent_id_tree", treeNode.getId());
            craqw.eq("parent_id_table_head", tableHead.getId());
            craqw.eq("index_id", indexId);
            craqw.eq("object_id", objectId);
            craqw.eq("data_type", "dky");
            craqw.eq("date_type", "allday");

            // 此方法会被调用两次，只有首次执行删除操作。
            if (ChoreUtil.isAlpha(mx)) {
                integralityOneLevelMapper.delete(craqw);
            }

            int i = 0;
            for (IntegralityOneLevel calResultAll : calResultAlls) {
                integralityOneLevelMapper.insert(calResultAll);
                if (i != 0 && i%batchSize == 0){
                    sqlSession.commit();
                    log.info("[{}/{}] 电科院{}_一级详情", i, calResultAlls.size(),indexId);
                }
                i++;
            }
            // 提交数据
            sqlSession.commit();
            log.info("[{}/{}] 电科院{}_一级详情", i, calResultAlls.size(),indexId);

        }catch (Exception e){
            sqlSession.rollback();
            e.printStackTrace();
            log.error("任务出错!"+e);
        }finally {
            sqlSession.close();
        }
        return CompletableFuture.completedFuture("success");
    }

    /**
     * 详情二
     * @param treeNode
     * @param managementUnits
     * @param param
     * @param tableHead
     * @param mx
     * @param objectId
     * @param tableNameCenter
     */
    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Object> dkyAccessRateTwoLevelContext(TreeNode treeNode, List<ManagementUnit> managementUnits, List<AssetTypeParamCenter> param,
                                             TableHead tableHead, String mx, String objectId, String tableNameCenter, String indexId) {
        List<Map<String, Object>> integrality = null;

        // 断面特殊处理，每个资产类型要单独跑一边，边循环边整合
        if (objectId.equals("0401")) {
            for (AssetTypeParamCenter map : param) {
                // 资产id
                String assetId = map.getAssetId();
                // 查询资产相关的设备id
                List<FractureSurfaceOrc> fractureSurfaceOrcs = fractureSurfaceOrcMapper.selectList(new QueryWrapper<FractureSurfaceOrc>().eq("asset_id", assetId));
                if (integrality == null) {
                    integrality = mapper.integralityTwoLevel(tableNameCenter , ChoreUtil.strToArray(assetId) , mx, managementUnits, fractureSurfaceOrcs);
                    continue;
                }
                // 合并数据
                integrality = ChoreUtil.mergeDataIntegrality(integrality, mapper.integralityTwoLevel(tableNameCenter,  ChoreUtil.strToArray(assetId), mx, managementUnits, fractureSurfaceOrcs));
            }
        }else{
            integrality = mapper.integralityTwoLevel(tableNameCenter,  param,  mx, managementUnits, null);
        }

        String now = DateUtil.now();

        //转换为对象
        List<IntegralityTwoLevel> calResultAlls = new ArrayList<IntegralityTwoLevel>();
        for (Map<String, Object> som : integrality) {
            IntegralityTwoLevel cra = new IntegralityTwoLevel();
            cra.setParentIdTree(treeNode.getId()+"");
            if (mx != null && mx.equals("mx")) {
                cra.setDatasource("0021990101_no");
            }else{
                cra.setDatasource(som.get("id")+"");
            }
            cra.setParentIdTableHead(tableHead.getId()+"");
            cra.setAssetId(som.get("asset_id")+"");
            cra.setAssetName(som.get("asset_name")+"");
            cra.setUpdensityName(som.get("name")+"");
            cra.setIndexId(indexId);
            if (indexId.equals("1001")) {
                cra.setValue(som.get("access_rate")+"");
                cra.setMolecule(som.get("actual_asset_num")+"");
                cra.setDenominator(som.get("asset_num")+"");
            } else if (indexId.equals("1002")) {
                cra.setValue(som.get("days_rate")+"");
                cra.setMolecule(som.get("actual_days_num")+"");
                cra.setDenominator(som.get("days_num")+"");
            } else if (indexId.equals("1004")) {
                cra.setValue(som.get("asset_num")+"");
                cra.setMolecule(som.get("asset_num")+"");
                cra.setDenominator(som.get("asset_num")+"");
            }
            cra.setObjectId(objectId);
            cra.setDataType("dky");
            cra.setDateType("allday");
            cra.setBeforeTime(som.get("request_first_time")+"");
            cra.setExaminationTime(som.get("gov_date")+"");
            cra.setDevId(som.get("dev_id")+"");
            cra.setDevName(som.get("dev_name")+"");
            cra.setCreateTime(now);
            calResultAlls.add(cra);
        }

        // 关闭session的自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        IntegralityTwoLevelMapper integralityTwoLevelMapper = sqlSession.getMapper(IntegralityTwoLevelMapper.class);

        try{
            QueryWrapper<IntegralityTwoLevel> craqw = new QueryWrapper<>();
            craqw.eq("parent_id_tree", treeNode.getId());
            craqw.eq("parent_id_table_head", tableHead.getId());
            craqw.eq("index_id", indexId);
            craqw.eq("object_id", objectId);
            craqw.eq("data_type", "dky");
            craqw.eq("date_type", "allday");

            // 此方法会被调用两次，只有首次执行删除操作。
            if (ChoreUtil.isAlpha(mx)) {
                integralityTwoLevelMapper.delete(craqw);
            }

            int i = 0;
            for (IntegralityTwoLevel calResultAll : calResultAlls) {
                integralityTwoLevelMapper.insert(calResultAll);
                if (i != 0 && i%batchSize == 0){
                    sqlSession.commit();
                    log.info("[{}/{}] 电科院{}_二级详情", i, calResultAlls.size(),indexId);
                }
                i++;
            }
            // 提交数据
            sqlSession.commit();
            log.info("[{}/{}] 电科院{}_二级详情", i, calResultAlls.size(),indexId);

        }catch (Exception e){
            sqlSession.rollback();
            e.printStackTrace();
            log.error("任务出错!"+e);
        }finally {
            sqlSession.close();
        }
        return CompletableFuture.completedFuture("success");
    }


    //===================================================电科院准确率===================================================================

//    @Async("threadPoolTaskExecutor") 由于大部分对象的准确率详表数据量过大，查询较慢，所以停止异步处理。
    public CompletableFuture<Object> accuracyRate(TreeNode treeNode, List<ManagementUnit> managementUnits, List<AssetTypeParamCenter> param,
                             Map<String, Object> tableNameMap, TableHead tableHead, String mx, String indexId){
        // 校验
        if(ChoreUtil.checkParam(treeNode,tableHead,managementUnits,param)) return CompletableFuture.completedFuture("error");
        // 查询的基本参数
        String tableNameCenter = tableNameMap.get(tableHead.getObjectId()).toString();   // 表名
        log.info("表名：{}，参数：{}",tableNameCenter, param);

        Map<String, Object> objectEng = ChoreUtil.getTableNameMap(accMapper.queryObjectEngAll()); // 对象英文名集合
        String objectEngStr = objectEng.get(tableHead.getObjectId()).toString();

        List<Future<Map<String, List<Map<String, Object>>>>> futures = new ArrayList<>();

        // 日期范围
        List<DateTime> rangeToList = DateUtil.rangeToList(DateUtil.parse(treeNode.getCalStartTime()), DateUtil.parse(treeNode.getCalEndTime()), DateField.DAY_OF_YEAR);
        for (DateTime dateTime : rangeToList) {

            String dt = dateTime.toDateStr();

            Callable<Map<String, List<Map<String, Object>>>> task = () -> {

//                Integer count = accMapper.queryDataCount(objectEngStr, dt);
//                if(count == 0) return null;

                List<Map<String, Object>> accuracyRateDay = null; // 单日准确率
                if (tableHead.getObjectId().equals("0401")) {// 断面特殊处理
                    for (AssetTypeParamCenter map : param) {
                        // 资产id
                        String assetId = map.getAssetId();
                        // 查询资产相关的设备id
                        List<FractureSurfaceOrc> fractureSurfaceOrcs = fractureSurfaceOrcMapper.selectList(new QueryWrapper<FractureSurfaceOrc>().eq("asset_id", assetId));
                        if (accuracyRateDay == null) {
                            accuracyRateDay = accMapper.queryAcc(objectEngStr, dt, param, managementUnits, mx, fractureSurfaceOrcs);
                            continue;
                        }
                        // 合并数据
                        accuracyRateDay = ChoreUtil.mergeDataaccuracyRate(accuracyRateDay, accMapper.queryAcc(objectEngStr, dt, ChoreUtil.strToArray(assetId), managementUnits, mx, fractureSurfaceOrcs));
                    }
                }else{
                    accuracyRateDay = accMapper.queryAcc(objectEngStr, dt, param, managementUnits, mx, null);
                }

                Map<String, List<Map<String, Object>>> mapR = new HashMap<>();
                mapR.put(dt, accuracyRateDay);
                return mapR;
            };

            Future<Map<String, List<Map<String, Object>>>> future = thread.submit(task);
            futures.add(future);
        }

        log.info("准确率任务添加成功，共{}个。",futures.size());

        QueryWrapper<CalResultAll> craqw = new QueryWrapper<>();
        craqw.eq("parent_id_tree", treeNode.getId());
        craqw.eq("parent_id_tablehead", tableHead.getId());
        craqw.eq("parent_id_index", indexId);
        craqw.eq("object_id",tableHead.getObjectId());
        craqw.eq("data_type", "dky");
        craqw.eq("date_type", "day");

        if (ChoreUtil.isAlpha(mx)) { // 此方法会被调用两次，只有首次执行删除操作。
            calResultAllMapper.delete(craqw);
        }

        String now = DateUtil.now();
        List<CalResultAll> accuracyRateAllDay = null; // 考核范围内的总准确率
        int dayNum = 0;// 多少天是有数的

        for (Future<Map<String, List<Map<String, Object>>>> future : futures) { // 考核范围内的每一天
            try {
                if(future.get() == null) continue;
                dayNum ++;
                Map<String, List<Map<String, Object>>> accuracyRateDay = future.get();
                if(accuracyRateDay.size() == 0) continue;
                List<CalResultAll> calResultAlls = new ArrayList<CalResultAll>();
                for (String key : accuracyRateDay.keySet()) {

                    if(key == null || key.equals("")) continue;

                    List<Map<String, Object>> mapList = accuracyRateDay.get(key);
                    for (Map<String, Object> som : mapList) {
                        CalResultAll cra = new CalResultAll();
                        cra.setParentIdTree(treeNode.getId()+"");
                        if (mx != null && mx.equals("mx")) {
                            cra.setDatasource("0021990101_no");
                        }else{
                            cra.setDatasource(som.get("datasource")+"");
                        }
                        cra.setParentIdTablehead(tableHead.getId()+"");
                        cra.setParentIdIndex(indexId);
                        BigDecimal fenziB = new BigDecimal(som.get("fenzi")+"");
                        BigDecimal fenmuB = new BigDecimal(som.get("fenmu")+"");
                        BigDecimal divide = fenziB.divide(fenmuB, 4, BigDecimal.ROUND_DOWN);
                        BigDecimal value = ChoreUtil.one.subtract(divide).multiply(ChoreUtil.oneHundred).setScale(2, BigDecimal.ROUND_DOWN);
                        String valueS = value.toString().equals("100.00")?"100":value.toString();
                        cra.setValue(valueS);
                        cra.setMolecule(som.get("fenzi")+"");
                        cra.setDenominator(som.get("fenmu")+"");
                        cra.setObjectId(tableHead.getObjectId());
                        cra.setDataType("dky");
                        cra.setDateType("day");
                        cra.setDayTime(key);
                        cra.setCreateTime(now);
                        calResultAlls.add(cra);
                    }
                }

                accuracyRateAllDay = ChoreUtil.mergeDataaccuracyRateAllDay(accuracyRateAllDay, calResultAlls); // 每天加和

                // 关闭session的自动提交
                SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
                CalResultAllMapper calResultAllMapper = sqlSession.getMapper(CalResultAllMapper.class);

                try{
                    if(calResultAlls.size() == 0) continue;
                    int i = 0;
                    for (CalResultAll calResultAll : calResultAlls) {
                        calResultAllMapper.insert(calResultAll);
                        if (i != 0 && i%batchSize == 0){
                            sqlSession.commit();
                            log.info("[{}/{}] dky{}_总数", i, calResultAlls.size(),indexId);
                        }
                        i++;
                    }
                    // 提交数据
                    sqlSession.commit();
                    log.info("[{}/{}] dky{}_总数", i, calResultAlls.size(),indexId);

                }catch (Exception e){
                    sqlSession.rollback();
                    e.printStackTrace();
                    log.error("任务出错!"+e);
                }finally {
                    sqlSession.close();
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if(accuracyRateAllDay == null){
            log.info("无数据");
            return CompletableFuture.completedFuture("error");
        }

        //转换为对象
        List<CalResultAll> calResultAlls = new ArrayList<CalResultAll>();
        for (CalResultAll som : accuracyRateAllDay) {
            CalResultAll cra = new CalResultAll();
            cra.setParentIdTree(treeNode.getId()+"");
            if (mx != null && mx.equals("mx")) {
                cra.setDatasource("0021990101_no");
            }else{
                cra.setDatasource(som.getDatasource());
            }
            cra.setParentIdTablehead(tableHead.getId()+"");
            cra.setParentIdIndex(indexId);
            if(som.getOverallRatio() == null) som.setOverallRatio(new BigDecimal("100"));
            BigDecimal cR = som.getOverallRatio().divide(new BigDecimal(dayNum), 4, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_DOWN);
            String valueS = cR.toString().equals("100.00")?"100":cR.toString();
            cra.setValue(valueS);
            cra.setMolecule(som.getMolecule());
            cra.setDenominator(som.getDenominator());
            cra.setObjectId(som.getObjectId());
            cra.setDataType("dky");
            cra.setDateType("allday");
            calResultAlls.add(cra);
        }

        QueryWrapper<CalResultAll> craqwAll = new QueryWrapper<>();
        craqwAll.eq("parent_id_tree", treeNode.getId());
        craqwAll.eq("parent_id_tablehead", tableHead.getId());
        craqwAll.eq("parent_id_index", indexId);
        craqwAll.eq("object_id",tableHead.getObjectId());
        craqwAll.eq("data_type", "dky");
        craqwAll.eq("date_type", "allday");

        // 此方法会被调用两次，只有首次执行删除操作。
        if (ChoreUtil.isAlpha(mx)) {
            calResultAllMapper.delete(craqwAll);
        }


        // 关闭session的自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        CalResultAllMapper calResultAllMapper = sqlSession.getMapper(CalResultAllMapper.class);

        try{

            int i = 0;
            for (CalResultAll calResultAll : calResultAlls) {
                calResultAllMapper.insert(calResultAll);
                if (i != 0 && i%batchSize == 0){
                    sqlSession.commit();
                    log.info("[{}/{}] 电科院{}_总数", i, calResultAlls.size(),indexId);
                }
                i++;
            }
            // 提交数据
            sqlSession.commit();
            log.info("[{}/{}] 电科院{}_总数", i, calResultAlls.size(),indexId);

        }catch (Exception e){
            sqlSession.rollback();
            e.printStackTrace();
            log.error("任务出错!"+e);
        }finally {
            sqlSession.close();
        }

        if (mx == null) {
            accuracyRate(treeNode, managementUnits, param, tableNameMap, tableHead, "mx", indexId);
        }
        return CompletableFuture.completedFuture("success");
    }

//    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Object> accuracyRateDetails(TreeNode treeNode, List<ManagementUnit> managementUnits, List<AssetTypeParamCenter> param,
                                    Map<String, Object> tableNameMap, TableHead tableHead, String mx, String indexId){
        // 校验
        if(ChoreUtil.checkParam(treeNode,tableHead,managementUnits,param)) return CompletableFuture.completedFuture("error");
        // 查询的基本参数
        String tableNameCenter = tableNameMap.get(tableHead.getObjectId()).toString();   // 表名
        log.info("表名：{}，参数：{}",tableNameCenter, param);

        Map<String, Object> objectEng = ChoreUtil.getTableNameMap(accMapper.queryObjectEngAll()); // 对象英文名集合
        String objectEngStr = objectEng.get(tableHead.getObjectId()).toString();

        Map<String, Object> assetRuleNum = ChoreUtil.getTableNameMap(mapper.queryAssetRuleNum()); // 资产对应规则数

        List<Future<Map<String, List<Map<String, Object>>>>> futures = new ArrayList<>();

        // 日期范围
        List<DateTime> rangeToList = DateUtil.rangeToList(DateUtil.parse(treeNode.getCalStartTime()), DateUtil.parse(treeNode.getCalEndTime()), DateField.DAY_OF_YEAR);
        for (DateTime dateTime : rangeToList) {

            String dt = dateTime.toDateStr();

            Callable<Map<String, List<Map<String, Object>>>> task = () -> {

//                Integer count = accMapper.queryDataCount(objectEngStr, dt);
//                if(count == 0) return null;

                List<Map<String, Object>> accuracyRateDay = null; // 单日准确率
                if (tableHead.getObjectId().equals("0401")) {// 断面特殊处理
                    for (AssetTypeParamCenter map : param) {
                        // 资产id
                        String assetId = map.getAssetId();
                        // 查询资产相关的设备id
                        List<FractureSurfaceOrc> fractureSurfaceOrcs = fractureSurfaceOrcMapper.selectList(new QueryWrapper<FractureSurfaceOrc>().eq("asset_id", assetId));
                        if (accuracyRateDay == null) {
                            accuracyRateDay = accMapper.queryAccDetails(objectEngStr, dt, param, managementUnits, mx, fractureSurfaceOrcs);
                            continue;
                        }
                        // 合并数据
                        accuracyRateDay = ChoreUtil.mergeDataaccuracyRate(accuracyRateDay, accMapper.queryAccDetails(objectEngStr, dt, ChoreUtil.strToArray(assetId), managementUnits, mx, fractureSurfaceOrcs));
                    }
                }else{
                    accuracyRateDay = accMapper.queryAccDetails(objectEngStr, dt, param, managementUnits, mx, null);
                }

                for (Map<String, Object> map : accuracyRateDay) { // 关联规则数
                    Object assetId = map.get("asset_id");
                    if(null == assetId) continue;
                    Object num = assetRuleNum.get(assetId);
                    if (null == num) {
                        map.put("rule_num", "0");
                    }else{
                        map.put("rule_num", num);
                    }

                }

                Map<String, List<Map<String, Object>>> mapR = new HashMap<>();
                mapR.put(dt, accuracyRateDay);
                return mapR;
            };

            Future<Map<String, List<Map<String, Object>>>> future = thread.submit(task);
            futures.add(future);
        }

        log.info("准确率任务添加成功，共{}个。",futures.size());

        QueryWrapper<AccuracyRateContent> craqw = new QueryWrapper<>();
        craqw.eq("parent_id_tree", treeNode.getId());
        craqw.eq("parent_id_table_head", tableHead.getId());
        craqw.eq("object_id",tableHead.getObjectId());
        craqw.eq("data_type", "dky");
        craqw.eq("date_type", "day");

        if (ChoreUtil.isAlpha(mx)) { // 此方法会被调用两次，只有首次执行删除操作。
            accuracyRateContentMapper.delete(craqw);
        }

        String now = DateUtil.now();

        for (Future<Map<String, List<Map<String, Object>>>> future : futures) { // 考核范围内的每一天
            try {
                if(future.get() == null) continue;
                Map<String, List<Map<String, Object>>> accuracyRateDay = future.get();

                List<AccuracyRateContent> calResultAlls = new ArrayList<AccuracyRateContent>();
                for (String key : accuracyRateDay.keySet()) {

                    List<Map<String, Object>> mapList = accuracyRateDay.get(key);
                    for (Map<String, Object> som : mapList) {
                        AccuracyRateContent cra = new AccuracyRateContent();
                        cra.setParentIdTree(treeNode.getId()+"");
                        if (mx != null && mx.equals("mx")) {
                            cra.setDatasource("0021990101_no");
                        }else{
                            cra.setDatasource(som.get("datasource")+"");
                        }
                        cra.setParentIdTableHead(tableHead.getId()+"");
                        BigDecimal fenziB = new BigDecimal(som.get("fenzi")+"");
                        BigDecimal fenmuB = new BigDecimal(som.get("fenmu")+"");
                        BigDecimal divide = fenziB.divide(fenmuB, 4, BigDecimal.ROUND_DOWN);
                        BigDecimal value = ChoreUtil.one.subtract(divide).multiply(ChoreUtil.oneHundred).setScale(2, BigDecimal.ROUND_DOWN);
                        String valueS = value.toString().equals("100.00")?"100":value.toString();
                        cra.setValue(valueS);
                        cra.setMolecule(som.get("fenzi")+"");
                        cra.setDenominator(som.get("fenmu")+"");
                        cra.setObjectId(tableHead.getObjectId());
                        cra.setDataType("dky");
                        cra.setDateType("day");
                        cra.setDayTime(key);
                        cra.setRelevanceRuleNum(som.get("rule_num")+"");
                        cra.setAssetName(som.get("asset_name")+"");
                        cra.setAssetId(som.get("asset_id")+"");
                        cra.setCreateTime(now);
                        calResultAlls.add(cra);
                    }
                }

                // 关闭session的自动提交
                SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
                AccuracyRateContentMapper accuracyRateContentMapper = sqlSession.getMapper(AccuracyRateContentMapper.class);

                try{
                    if(calResultAlls.size() == 0) continue;
                    int i = 0;
                    for (AccuracyRateContent calResultAll : calResultAlls) {
                        accuracyRateContentMapper   .insert(calResultAll);
                        if (i != 0 && i%batchSize == 0){
                            sqlSession.commit();
                            log.info("[{}/{}] dky{}_总数", i, calResultAlls.size(),indexId);
                        }
                        i++;
                    }
                    // 提交数据
                    sqlSession.commit();
                    log.info("[{}/{}] dky{}_总数", i, calResultAlls.size(),indexId);

                }catch (Exception e){
                    sqlSession.rollback();
                    e.printStackTrace();
                    log.error("任务出错!"+e);
                }finally {
                    sqlSession.close();
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (mx == null) {
            accuracyRateDetails(treeNode, managementUnits, param, tableNameMap, tableHead, "mx", indexId);
        }
        return CompletableFuture.completedFuture("success");
    }


    //===================================================南瑞完整率===================================================================
    /**
     * 南瑞完整率
     */
    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Object> nrAccessRate(TreeNode treeNode, List<ManagementUnit> managementUnits, List<AssetTypeParamCenter> param,
                             TableHead tableHead, String dkyObjectId, String indexId){

        String currentTime = DateUtil.now().substring(0, 10);

        List<CalResultAll> calResultAlls = new ArrayList<>();

        try {

            // 校验
            if(ChoreUtil.checkParam(treeNode,tableHead,managementUnits,param)) return CompletableFuture.completedFuture("error");

            for (ManagementUnit unit : managementUnits) {

                Map<String,Object> nrP = new HashMap<>();
                nrP.put("objType", tableHead.getObjectId());

                // 开始时间若不为空，则再传参过程中传递start、endTime字段
                if (!ChoreUtil.isAlpha(treeNode.getCalStartTime())) {
                    nrP.put("startTime", treeNode.getCalStartTime());
                    nrP.put("endTime", currentTime);
                }

                if ("7 8".contains(dkyObjectId)) {
                    log.info("允许使用时间: "+dkyObjectId);
                    nrP.put("startTime", "2017-01-01");
                    nrP.put("endTime", currentTime);
                }


                BigDecimal fenZi = new BigDecimal("0"); // 分子
                BigDecimal fenMu = new BigDecimal("0"); // 分母

                if (unit.getDatasource().equals("0021990101")) {
                    for (String str : "0021990101".split(",")) {
                        nrP.put("datasourceId", str);
                        CompletableFuture<Map<String, BigDecimal>> mapCompletableFuture = nrStripping(param, tableHead.getObjectId(), nrP, indexId);
                        if (mapCompletableFuture == null || mapCompletableFuture.get() == null) continue;
                        Map<String, BigDecimal> sbm = mapCompletableFuture.get(); // 为适配华北=华北+蒙西的逻辑，将方法中的逻辑拆分。
                        fenZi = fenZi.add(sbm.get("fenZi"));
                        fenMu = fenMu.add(sbm.get("fenMu"));
                    }
                } else if(unit.getDatasource().equals("0021990101_no")){
                    nrP.put("datasourceId", "0021990101");
                    CompletableFuture<Map<String, BigDecimal>> mapCompletableFuture = nrStripping(param, tableHead.getObjectId(), nrP, indexId);
                    if (mapCompletableFuture == null || mapCompletableFuture.get() == null) continue;
                    Map<String, BigDecimal> sbm = mapCompletableFuture.get();
                    fenZi = fenZi.add(sbm.get("fenZi"));
                    fenMu = fenMu.add(sbm.get("fenMu"));
                } else {
                    nrP.put("datasourceId", unit.getDatasource());
                    CompletableFuture<Map<String, BigDecimal>> mapCompletableFuture = nrStripping(param, tableHead.getObjectId(), nrP, indexId);
                    if (mapCompletableFuture == null || mapCompletableFuture.get() == null) continue;
                    Map<String, BigDecimal> sbm = mapCompletableFuture.get();
                    fenZi = fenZi.add(sbm.get("fenZi"));
                    fenMu = fenMu.add(sbm.get("fenMu"));
                }

                if(fenMu.compareTo(BigDecimal.ZERO) == 0) continue;

                // 结算
                BigDecimal closeAnAccount = fenZi.divide(fenMu, 4, BigDecimal.ROUND_DOWN).multiply(ChoreUtil.oneHundred).setScale(2, BigDecimal.ROUND_DOWN);

                CalResultAll cra = new CalResultAll();
                cra.setParentIdTree(treeNode.getId()+"");
                cra.setDatasource(unit.getDatasource());
                cra.setParentIdTablehead(tableHead.getId()+"");
                cra.setParentIdIndex(indexId);
                if (indexId.equals("1004")) {
                    cra.setValue(fenMu.stripTrailingZeros().toPlainString());
                    cra.setMolecule(fenMu.stripTrailingZeros().toPlainString());
                    cra.setDenominator(fenMu.stripTrailingZeros().toPlainString());
                }else{
                    cra.setValue(closeAnAccount.stripTrailingZeros().toPlainString());
                    cra.setMolecule(fenZi.stripTrailingZeros().toPlainString());
                    cra.setDenominator(fenMu.stripTrailingZeros().toPlainString());
                }
                cra.setObjectId(dkyObjectId);
                cra.setDataType("nr");
                cra.setDateType("allday");
                calResultAlls.add(cra);

            }

            // 关闭session的自动提交
            SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
            CalResultAllMapper calResultAllMapper = sqlSession.getMapper(CalResultAllMapper.class);

            try{

                QueryWrapper<CalResultAll> craqw = new QueryWrapper<>();
                craqw.eq("parent_id_tree", treeNode.getId());
                craqw.eq("parent_id_tablehead", tableHead.getId());
                craqw.eq("parent_id_index", indexId);
                craqw.eq("object_id",tableHead.getObjectId());
                craqw.eq("data_type", "nr");
                craqw.eq("date_type", "allday");

                calResultAllMapper.delete(craqw);

                int i = 0;
                for (CalResultAll calResultAll : calResultAlls) {
                    calResultAllMapper.insert(calResultAll);
                    if (i != 0 && i%batchSize == 0){
                        sqlSession.commit();
                        log.info("[{}/{}] 南瑞{}_总数", i, calResultAlls.size(),indexId);
                    }
                    i++;
                }
                // 提交数据
                sqlSession.commit();
                log.info("[{}/{}] 南瑞{}_总数", i, calResultAlls.size(),indexId);

            }catch (Exception e){
                sqlSession.rollback();
                e.printStackTrace();
                log.error("任务出错!"+e);
            }finally {
                sqlSession.close();
            }


        } catch (Exception e) {
            e.printStackTrace();
            log.error("南瑞接入率计算错误!");
            log.error(""+e);
        }
        return CompletableFuture.completedFuture("success");
    }

    @Async("threadPoolTaskExecutor")
    public CompletableFuture<Map<String, BigDecimal>> nrStripping(List<AssetTypeParamCenter> param, String nrObjectId, Map<String, Object> nrP, String indexId){
        Map<String, BigDecimal> result = new HashMap<>();

        BigDecimal fenZi = new BigDecimal("0"); // 资产数量
        BigDecimal fenMu = new BigDecimal("0"); // 应上送资产数量

        List<Map<String, Object>> nrHttpResultSpc = null;
        Map<String, Object> nrHttpResult = null;

        for (AssetTypeParamCenter map : param) {
            nrP.put("dataType", map.getAssetId());

            for (int i = 1; i <= 3; i++){ //日、月、年
                nrP.put("dateType", i+"");

                if (nrObjectId.equals("1") || nrObjectId.equals("4")) { // 电网和直流输电系统的特殊处理

                    try{
                        if (indexId.equals("1001")) {
                            nrHttpResultSpc = restTemplateUtil.accessRatePostSpecial(JSON.toJSONString(nrP));
                        } else if (indexId.equals("1002")) {
                            nrHttpResultSpc = restTemplateUtil.integrityRatePostSpecial(JSON.toJSONString(nrP));
                        } else if (indexId.equals("1004")) {
                            nrHttpResultSpc = restTemplateUtil.accessRatePostSpecial(JSON.toJSONString(nrP));
                        }
                    }catch (Exception e){
                        e.printStackTrace();
                        log.error(""+e);
                    }

                    if(nrHttpResultSpc == null || nrHttpResultSpc.size() == 0){
                        log.error("调用南瑞电量http接口发现无数据返回，调用参数如下");
                        log.error(nrP.toString());
                        continue;
                    }

                    for (Map<String, Object> nhr : nrHttpResultSpc) {
                        fenZi = fenZi.add(new BigDecimal(nhr.get("actualNum")+""));
                        fenMu = fenMu.add(new BigDecimal(nhr.get("totalNum")+""));
                    }

                }else{

                    try{
                        if (indexId.equals("1001")) {
                            nrHttpResult = restTemplateUtil.accessRatePost(JSON.toJSONString(nrP));
                        } else if (indexId.equals("1002")) {
                            nrHttpResult = restTemplateUtil.integrityRatePost(JSON.toJSONString(nrP));
                        } else if (indexId.equals("1004")) {
                            nrHttpResult = restTemplateUtil.accessRatePost(JSON.toJSONString(nrP));
                        }
                    }catch (Exception e){
                        e.printStackTrace();
                        log.error(""+e);
                    }

                    if(nrHttpResult == null || nrHttpResult.size() == 0){
                        log.error("调用南瑞电量http接口发现无数据返回，调用参数如下");
                        log.error(nrP.toString());
                        continue;
                    }

                    fenZi = fenZi.add(new BigDecimal(nrHttpResult.get("actualNum")+""));
                    fenMu = fenMu.add(new BigDecimal(nrHttpResult.get("totalNum")+""));

                }
            }
        }

        result.put("fenZi",fenZi);
        result.put("fenMu",fenMu);

        return CompletableFuture.completedFuture(result);
    }

}
